﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Midi;
using System.Runtime.InteropServices;

namespace MIDIRemapper.DeviceInterfaces
{
    partial class DeviceInterface
    {
        InputDevice inputDevice;
        OutputDevice outputDevice;

        public string outputDeviceName
        {
            get
            {
                return outputDevice == null ? null : outputDevice.Name;
            }
        }

        public string inputDeviceName
        {
            get
            {
                return inputDevice == null ? null : inputDevice.Name;
            }
        }
        
        int outputDeviceId;
        int inputDeviceId;
        List<int> newValues = new List<int>();

        public DeviceInterface()
        {
            for (int i = 0; i <= 4; i++) { newValues.Add(5); }
            inputDevice = null;
            outputDevice = null;
        }

        /// <summary>
        /// Resets the internal lists of input and output devices. 
        /// </summary>
        public static void ResetDeviceLists()
        {
            InputDevice.RefreshInstalledDevices();
            OutputDevice.RefreshInstalledDevices();
        }

        public bool UnloadOutputDevice()
        {
            if(outputDevice!=null)
            {
                if(outputDevice.IsOpen)
                {
                    try
                    {
                    outputDevice.Close();
                    }
                    catch (DeviceException) { return false; }
                    return true;
                }
            }
            return false;
        }

        public bool UnloadInputDevice()
        {
            if (inputDevice != null)
            {
                if (inputDevice.IsReceiving)
                {
                    inputDevice.StopReceiving();
                }
                if (inputDevice.IsOpen)
                {
                    try
                    {
                        inputDevice.Close();
                    }
                    catch (DeviceException) { return false; }
                    return true;
                }
            }
            return false;
        }

        public void LoadOutputDevice(int deviceId)
        {
            outputDeviceId = deviceId;
            _LoadOutputDevice();
        }

        public void LoadInputDevice(int deviceId)
        {
            inputDeviceId = deviceId;
            _LoadInputDevice();
        }

        /// <summary>
        /// Load output device based on name
        /// </summary>
        /// <param name="name">String to match an OutpuDevice.Name</param>
        /// <returns>Matching device has been found.</returns>
        public bool LoadOutputDevice(string name, out int index)
        {
            foreach (OutputDevice od in OutputDevice.InstalledDevices)
            {
                if (od.Name == name)
                {
                    outputDeviceId = OutputDevice.InstalledDevices.IndexOf(od);
                    try { _LoadOutputDevice(); }
                    catch (DeviceException)
                    {
                        outputDevice = null;
                        index = -1;
                        return false;
                    }
                    catch (InvalidOperationException)
                    {
                        outputDevice = null;
                        index = -1;
                        return false;
                    }
                    index = outputDeviceId;
                    return true;
                }
            }
            index = -1;
            return false;
        }

        /// <summary>
        /// Load input device based on name
        /// </summary>
        /// <param name="name"></param>
        /// <returns>Matching device has been found.</returns>
        public bool LoadInputDevice(string name, out int index)
        {
            foreach (InputDevice id in InputDevice.InstalledDevices)
            {
                if (id.Name == name)
                {
                    inputDeviceId = InputDevice.InstalledDevices.IndexOf(id);

                    try { _LoadInputDevice(); }
                    catch (DeviceException)
                    {
                        inputDevice = null;
                        index = -1;
                        return false;
                    }
                    catch (InvalidOperationException)
                    {
                        outputDevice = null;
                        index = -1;
                        return false;
                    }
                    index = inputDeviceId;
                    return true;
                }
            }
            index = -1;
            return false;
        }

        #region Private methods
        private void _LoadOutputDevice()
        {
            outputDevice = OutputDevice.InstalledDevices[outputDeviceId];
            outputDevice.Open();
            if (inputDevice != null)
            {
                if (!inputDevice.IsOpen) inputDevice.Open();

                if (!inputDevice.IsReceiving) inputDevice.StartReceiving(new Clock(1000));
            }

            Program.form.DisplayStatus("Successfully loaded: " + outputDevice.Name);
        }
        private void _LoadInputDevice()
        {
            inputDevice = InputDevice.InstalledDevices[inputDeviceId];
            if (!inputDevice.IsOpen) inputDevice.Open();

            inputDevice.NoteOn += inputDevice_NoteOn;
            inputDevice.NoteOff += inputDevice_NoteOff;
            inputDevice.ProgramChange += inputDevice_ProgramChange;
            inputDevice.ControlChange += inputDevice_ControlChange;
            inputDevice.PitchBend += inputDevice_PitchBend;
            if (outputDevice != null)
            {
                if (outputDevice.IsOpen && !inputDevice.IsReceiving)
                {
                    inputDevice.StartReceiving(new Clock(1000));
                }
            }

            Program.form.DisplayStatus("Successfully loaded: " + inputDevice.Name);
        }

        private void SendMessageByType(MidiType t, int program, Channel ch, int value)
        {
            if (t == MidiType.Pitch_Wheel)
            {
                //Combine value and program
                outputDevice.SendPitchBend(ch, (value << 7) | program);
            }
            else if (t == MidiType.Note_On)
            {
                outputDevice.SendNoteOn(ch, (Pitch)program, value);
            }
            else if (t == MidiType.Note_Off)
            {
                outputDevice.SendNoteOff(ch, (Pitch)program, value);
            }
            else if (t == MidiType.Instrument)
            {
                outputDevice.SendProgramChange(ch, (Instrument)program);
            }
            else if (t == MidiType.Control)
            {
                outputDevice.SendControlChange(ch, (Control)program, value);
            }
        }
        #endregion
    }
}
